<script setup lang="ts" generic="T">
import type { FormInstance } from 'element-plus'
import type { DataAction } from '../DataForm/types'
import type { DataTableProps } from './types'
import type { PageRes } from '@/apis/common/commonApi'
import { cloneDeep } from 'lodash-es'
import { computed, reactive, ref } from 'vue'
import { hasAnyAuth } from '@/utils/authUtil'

const { fields, rowActions } = defineProps<DataTableProps>()

const emits = defineEmits(['change', 'rowClick', 'rowSubmitSuccess', 'rowEditChange'])
const queryData = defineModel<PageRes | any>('queryData')
const tableData = defineModel<PageRes<T> | T[]>('tableData')

function sortChange(data: any) {
  if (data.order == null || data.prop == null) {
    queryData.value.orderAsc = undefined
    queryData.value.orderBy = undefined
  }
  else {
    queryData.value.orderAsc = data.order !== 'descending'
    queryData.value.orderBy = data.prop
  }
  emits('change')
}

function pageChange(currentPage: number, pageSize: number) {
  queryData.value.current = currentPage
  queryData.value.size = pageSize
  emits('change')
}

const rowButtons = computed(() => {
  return rowActions?.filter(a => a.authCode === undefined || hasAnyAuth(a.authCode))
})

const tableFormRef = ref<FormInstance>()
function openRowEdit(index: number, submit: ((data: T) => Promise<any>), data?: any) {
  const list = (Array.isArray(tableData.value) ? tableData.value : tableData.value?.records) as any[]

  if (data === undefined) {
    list[index]._edit = { old: cloneDeep(list[index]), submit }
  }
  else {
    list.splice(index, 0, { ...data, _edit: { submit } })
  }
  emits('rowEditChange', getRowIndexList(row => (row as any)._edit !== undefined))
}

const formItemPropPrefix = computed(() => Array.isArray(tableData.value) ? undefined : 'records')

async function submitRowEdit(index: number, handleSuccess = true) {
  const list = (Array.isArray(tableData.value) ? tableData.value : tableData.value?.records) as any[]
  const _edit = list[index]._edit
  if (_edit === undefined || _edit.submit === undefined) {
    return
  }
  const props: string[] = []
  for (const field of fields!) {
    props.push(formItemPropPrefix.value === undefined ? `${index}.${field.name}` : `${formItemPropPrefix.value}.${index}.${field.name}`)
  }
  const valid = await tableFormRef.value?.validateField(props).catch(() => false)
  if (valid) {
    const data = cloneDeep(list[index])
    delete data[_edit]
    const result = await _edit.submit(data)
    list[index] = result
    if (handleSuccess) {
      ElMessage.success('保存成功')
      emits('rowSubmitSuccess', result)
    }
    emits('rowEditChange', getRowIndexList(row => (row as any)._edit !== undefined))
    return result
  }
  else {
    throw new Error('校验失败')
  }
}

function cancelRowEdit(index: number) {
  const list = (Array.isArray(tableData.value) ? tableData.value : tableData.value?.records) as any[]
  const _edit = list[index]._edit
  if (_edit === undefined) {
    return
  }

  list[index]._edit = undefined
  if (_edit.old !== undefined) {
    list[index] = _edit.old
  }
  else {
    list.splice(index, 1)
  }
  emits('rowEditChange', getRowIndexList(row => (row as any)._edit !== undefined))
}

const editButtons = reactive<Record<string, DataAction>>({
  save: {
    label: '保存',
    type: 'primary',
    click: async (_: any, index?: number) => await submitRowEdit(index!),
  },
  cancel: {
    label: '取消',
    click: (_: any, index?: number) => cancelRowEdit(index!),
  },
})

function getRowIndexList(filter?: (row: T) => boolean) {
  const result: number[] = []
  const list = (Array.isArray(tableData.value) ? tableData.value : tableData.value?.records) as any[]
  list.forEach((row, index) => {
    if (filter === undefined || filter(row)) {
      result.push(index)
    }
  })
  return result
}

function openRowEditAll(submit: ((data: any) => Promise<any>)) {
  const notEditIndexList = getRowIndexList(row => (row as any)._edit === undefined)
  notEditIndexList.forEach(index => openRowEdit(index, submit))
}
function cancelRowEditAll() {
  while (true) {
    const editIndexList = getRowIndexList(row => (row as any)._edit !== undefined)
    if (editIndexList.length === 0) {
      return
    }
    cancelRowEdit(editIndexList[0])
  }
}

async function submitRowEditAll() {
  const valid = await tableFormRef.value?.validate().catch(() => false)
  if (!valid) {
    return
  }
  const editIndexList = getRowIndexList(row => (row as any)._edit !== undefined)
  if (editIndexList.length === 0)
    return
  for (const index of editIndexList) {
    await submitRowEdit(index, false)
  }
  ElMessage.success('保存成功')
  emits('rowSubmitSuccess')
}

defineExpose({
  openRowEdit,
  cancelRowEdit,
  submitRowEdit,

  openRowEditAll,
  cancelRowEditAll,
  submitRowEditAll,
})
</script>

<template>
  <div v-if="tableData">
    <el-form ref="tableFormRef" :model="tableData">
      <el-table
        v-if="fields"
        :data="Array.isArray(tableData) ? tableData : tableData.records"
        show-overflow-tooltip
        highlight-current-row
        stripe
        :border="true"
        :default-expand-all="expandRowKeys === undefined"
        :row-key="fields.find(c => c.columnType === 'KEY')?.name"
        :default-sort="queryData?.orderBy ? { prop: queryData.orderBy, order: queryData.orderAsc === true ? 'ascending' : 'descending' } : undefined"
        :expand-row-keys="expandRowKeys"
        :current-row-key="currentRowKey"
        @sort-change="sortChange"
        @row-click="(row) => emits('rowClick', row)"
      >
        <template v-for="f in fields" :key="f.name">
          <data-table-column v-if="(f.show === undefined || f.show) && f.columnFilter !== false" :field="f" :parent-prop="formItemPropPrefix">
            <template #[f.name]="slotProps">
              <slot :name="f.name" v-bind="slotProps" />
            </template>
            <template #[`edit_${f.name}`]="slotProps">
              <slot :name="`edit_${f.name}`" v-bind="slotProps" />
            </template>
          </data-table-column>
        </template>
        <el-table-column
          v-if="rowButtons && rowButtons.length > 0" label="操作" align="center" header-align="center" fixed="right"
          min-width="150px"
        >
          <template #default="{ row, $index }">
            <data-row-buttons
              v-if="row._edit !== undefined"
              :actions="[editButtons.save, editButtons.cancel]" :row="row" :row-index="$index"
            />
            <data-row-buttons
              v-else
              :actions="rowButtons" :row="row" :row-index="$index"
            />
          </template>
        </el-table-column>
      </el-table>
    </el-form>
    <el-pagination
      v-if="!Array.isArray(tableData) "
      v-model:current-page="tableData.current!" v-model:page-size="tableData.size!" :total="tableData.total"
      class="mt-2" background
      :hide-on-single-page="false"
      layout="->,prev,pager,next,total,sizes,jumper" @change="pageChange"
    />
  </div>
</template>
